// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"
#include "stdio.h"
#include "iostream.h"
#include "list.h"
//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
    
    while (value == 0) { 			// semaphore not available
	queue->Append((void *)currentThread);	// so go to sleep
	currentThread->Sleep();
    } 
    value--; 					// semaphore available, 
						// consume its value
    
    (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) {
    name = debugName;   //for debug purpose
    Owner = NULL;       //Intially No owner, Owner is assigned only after it has been
                        // Acuired.
    queue_lock = new List; //this queue holds the queue of thread pointers
}
//Lock::Lock(){}
Lock::~Lock() {
    delete queue_lock;
}

Condition::Condition(char* debugName) { 
	name = debugName;   //for debug purpose
	queue_condition = new List;
	//lock = new Lock("lockname");
}
//Condition::Condition(){}
Condition::~Condition() {
	delete queue_condition;
	delete lock;
}

// This function acquires the lock if no one has it else puts himeself to wait queue
//and goes to sleep.

int Lock::Acquire() {
    IntStatus interrupt_val;
    int retValue;

    //Disable Interrupts
    interrupt_val = interrupt->SetLevel(IntOff);
    
    //Check for the Lock Owner
    //yes
    if(isHeldByCurrentThread())
    {
	//cout<< "\nMsg 11: You aleady have the lock" << endl;
        //restore Interupts
        interrupt->SetLevel(interrupt_val);

        return 11;
    }//if

    //If Lock Available
    if(!IsAcquired)
    {

        //Make it Busy
        IsAcquired = true;

        //Make myself Owner
        Owner = currentThread;

	//cout<< "\nMsg 10: " << currentThread->getName()<<" You acquired the lock " << name << endl;
        retValue = 10;
	
    }
    //else //Lock is not available
    else
    {
	cout<< "\nYou can't have the lock now, so go to sleep \n";
        //Add Myself to Lock Wait Queue
        queue_lock->Append((void *)currentThread);
	currentThread->Sleep(); //Put Me on Sleep
        retValue = 12;
        
    }

    //Restore Interrupts
    interrupt->SetLevel(interrupt_val);
    return retValue;

}//Acquire

// This function Releases the lock only if the owner itself is trying to Release.
//gives the lock to the next thread waiting in the queue. If queue is empty then
// release the Lock.

int Lock::Release() {
    IntStatus interrupt_val;

    //Disable interrupt to make the operation Atomic
    interrupt_val = interrupt->SetLevel(IntOff);

    //If it is the Owners Lock
    if(!isHeldByCurrentThread()){
        cout<<"Your are not the owner \n";
        interrupt->SetLevel(interrupt_val);
        return 21;
    }else{

        Thread *thread;
        //IntStatus oldLevel = interrupt->SetLevel(IntOff);

        thread = (Thread *)queue_lock->Remove();
        if (thread != NULL){ // make thread ready
		cout<< "Handing over the lock to next in ready Q  \n";
            Owner = thread; //Make them the Lock Owner
            scheduler->ReadyToRun(thread); //remove it from the Locks wait queue
        }else{
	 //   cout<< "Lock Available " << name << "\n";
            IsAcquired = FALSE; //make lock available
            Owner = NULL; //Clear Lock Ownewship
        }

        (void) interrupt->SetLevel(interrupt_val); //restore Interrupts
        return 20;
    }
        //print message , restore interrupts and return

            

    

}

// This function puts the thread to sleep if waiting on some condition and adds all the
// threads waiting on the same condition to the condition wait queue.

int Condition::Wait(Lock* conditionLock){
	//cout<<"inside wait" << name << "\n";
    IntStatus interrupt_val = interrupt->SetLevel(IntOff); // Disable interrupt to make the operation Atomic
    
    /*if(!(conditionLock->isHeldByCurrentThread())){         // Check for ownership
        cout<<"You are not the owner";
        interrupt->SetLevel(interrupt_val);
        return;
    }else{ */
        if(queue_condition->IsEmpty()){                    // Save lock pointer for first waiter
            lock=conditionLock;
        }
        if(lock!=conditionLock){                           // Check whether lock's match or not
            cout<<"Locks are different from wait \n";
            interrupt->SetLevel(interrupt_val);
            return 41;
        }else{
           
            conditionLock->Release();                      // Exit monitor
            queue_condition->Append(currentThread);        // Add current thread to condition wait queue
            //cout << "Adding myself and going to sleep \n";
            currentThread->Sleep();
            //cout << "wake up time " << name << "\n";
            conditionLock->Acquire();                      // Re-Enter monitor
               // restore Interrupts
        }
  //  }
     (void) interrupt->SetLevel(interrupt_val); 
     return 40;
}

// This function wakes up one sleeping thread from the wait queue and
// puts it on the ready queue.

int Condition::Signal(Lock* conditionLock){
	//cout<<"Inside Signal "<< name << "\n";
    Thread *thread;
    IntStatus interrupt_val = interrupt->SetLevel(IntOff); // Disable interrupt to make the operation Atomic
    if(queue_condition->IsEmpty()){                        // If no waiters then restore interrupts and return
        cout<<"The condition queue is empty. No waiters";
        interrupt->SetLevel(interrupt_val);
        return 31;
    }
    if(lock!=conditionLock){                               // Verifying signalers lock with lock associated with CV
        cout<<"Locks are different for thread " << conditionLock->getName() << "," << currentThread->getName() <<"differ with "<<lock->getName() <<"\n\n\n" ;
        interrupt->SetLevel(interrupt_val);
        return 32;
    }
    thread=(Thread *)queue_condition->Remove();     // Remove one thread from the wait queue
    
    scheduler->ReadyToRun(thread);                  // Put it on ready queue

    //cout << "Awake the thread" << thread->getName() << "\n";
    if(queue_condition->IsEmpty()){                         // If no more waiters then clear lock relationship with CV
        lock=NULL;
    }
    (void) interrupt->SetLevel(interrupt_val);              // restore Interrupts
    return 30;
}

// This function wakes up all the sleeping threads

int Condition::Broadcast(Lock* conditionLock){
    Thread *thread;
    int retValue;
    while(!queue_condition->IsEmpty()){
        retValue = Signal(conditionLock);
    }
    return retValue;

}
//This functions check the owner of the lock against the current thread to see
//if they are same. If they are the same mean it is trying to acuire what he already
//has and notifies it by returning true.

bool Lock::isHeldByCurrentThread(){

    if(Owner == currentThread)
        return true;
    else
        return false;
}


//void Condition::Wait(Lock* conditionLock) { ASSERT(FALSE); }
//void Condition::Signal(Lock* conditionLock) { }
//void Condition::Broadcast(Lock* conditionLock) { }
